/*
	The MIT License

	Copyright (c) 2008 IFMO/GameDev Studio

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
*/

#include "maya_local.h"

#if 0
const float SAMPLE_RATE	=	1.0f;

/*-----------------------------------------------------------------------------
	Transformation and animation stuff :
-----------------------------------------------------------------------------*/

//
//	GetTransformMatricies	
//	see: MFnTransform for details :	
//
void GetTransformMatricies	( MObject &obj, 
	MMatrix	&Sp,	MMatrix &St,	MMatrix &S,		MMatrix	&Sh,
	MMatrix &Rp,	MMatrix	&Rt,	MMatrix	&Ro,	MMatrix &R,		MMatrix	&T	)
{																					
	MFnTransform	transform(obj);
	
	Sp	=	MMatrix::identity;
	St	=	MMatrix::identity;
	S	=	MMatrix::identity;
	Sh	=	MMatrix::identity;
	Rp	=	MMatrix::identity;
	Rt	=	MMatrix::identity;
	Ro	=	MMatrix::identity;
	R	=	MMatrix::identity;
	T	=	MMatrix::identity;
	
	MPoint		sp;		sp	=	transform.scalePivot( MSpace::kTransform );
	MVector		s;				transform.getScale( &s.x );
	MVector		sh;				transform.getShear( &sh.x );
	MPoint		spt;	spt	=	transform.scalePivotTranslation( MSpace::kTransform );
	MPoint		rp;		rp	=	transform.rotatePivot( MSpace::kTransform );
	MQuaternion	ro;		ro	=	transform.rotateOrientation( MSpace::kTransform );
	MQuaternion	r;				transform.getRotation( r, MSpace::kTransform );
	MPoint		rpt;	rpt	=	transform.rotatePivotTranslation( MSpace::kTransform );
	MVector		t;		t	=	transform.getTranslation( MSpace::kTransform );
	MTransformationMatrix::RotationOrder	rot_order;
	rot_order	=	 transform.rotationOrder();


	Sp(3,0)	=	sp.x / sp.w;		St(3,0)	=	spt.x / spt.w;
	Sp(3,1)	=	sp.y / sp.w;		St(3,1)	=	spt.y / spt.w;
	Sp(3,2)	=	sp.z / sp.w;		St(3,2)	=	spt.z / spt.w;
	
	S(0,0)	=	s.x;				Sh(1,0)	=	sh.x;
	S(1,1)	=	s.y;				Sh(2,0)	=	sh.y;
	S(2,2)	=	s.z;				Sh(2,1)	=	sh.z;
	
	Rp(3,0)	=	rp.x / rp.w;		Rt(3,0)	=	rpt.x / rpt.w;
	Rp(3,1)	=	rp.y / rp.w;		Rt(3,1)	=	rpt.y / rpt.w;
	Rp(3,2)	=	rp.z / rp.w;		Rt(3,2)	=	rpt.z / rpt.w;
	
	Ro	=	ro.asMatrix();
	R	=	r.asMatrix();
	
	T(3,0)	=	t.x;
	T(3,1)	=	t.y;
	T(3,2)	=	t.z;
}	


//
//	EMLevelExporter::ParseTransform
//
MStatus EMLevelExporter::ParseTransform( IPxLvNode lv_node, MObject &obj )
{
	MFnTransform	transform(obj);

	MMatrix		Sp;					MMatrix		St;
	MMatrix		S;					MMatrix		Sh;
	MMatrix		Rp;					MMatrix		Rt;
	MMatrix		Ro;
	MMatrix		R;
	MMatrix		T;
	
	GetTransformMatricies( obj, Sp, St, S, Sh, Rp, Rt, Ro, R, T );	

	MMatrix	finS	=	Sp.inverse() * S * Sh * Sp * St;
	MMatrix	finR	=	Rp.inverse() * Ro * R * Rp * Rt;
	MMatrix	finT	=	T;

	//	not used :	
	MMatrix	TM1	=	 finS * finR * finT; 
	
	lv_node->scale			=	MMatrix2EMatrix4( finS );
	lv_node->rotation		=	MMatrix2EMatrix4( finR );
	lv_node->translation	=	MMatrix2EMatrix4( finT );

	//	NOTE : geometry must be scaled before exporting, 
	//	so keep rotation and translation only :
	EMatrix4	TM			=	MMatrix2EMatrix4( finR * finT );

	EVec4	pos		=	EVec4( TM(3,0), TM(3,1), TM(3,2), TM(3,3) );
	EQuat	orient	=	QuatFromMatrix( MMatrix2EMatrix4( finR ) );	

	lv_node->entity_desc.SetValue("position", va("%g %g %g %g",	pos.x, pos.y, pos.z, pos.w));
	lv_node->entity_desc.SetValue("orient",   va("%g %g %g %g", orient.x, orient.y, orient.z, orient.w));

	//	Parse anim curves associated with this transform node :
	ParseAnimCurves( lv_node, obj );

	//	Make quaternions and positions :
	MakeTransformAnimation( lv_node, obj );	

	return MS::kSuccess;
}


//
//	EMLevelExporter::MakeTransformAnimation
//
MStatus EMLevelExporter::MakeTransformAnimation( IPxLvNode lv_node, MObject &obj )
{
	MFnTransform	transform(obj);
	
	//	get matricies :	
	MMatrix		Sp;					MMatrix		St;
	MMatrix		S;					MMatrix		Sh;
	MMatrix		Rp;					MMatrix		Rt;
	MMatrix		Ro;
	MMatrix		R;
	MMatrix		T;
	GetTransformMatricies( obj, Sp, St, S, Sh, Rp, Rt, Ro, R, T );	

	//	get rotation order :
	MTransformationMatrix::RotationOrder	rot_order;
	double		angles[3];
	transform.getRotation(angles, rot_order);
	
	MVector	t	=	transform.getTranslation(MSpace::kTransform);

	//	get 'translate' and 'rotate' curves :
	IPxAnimCurve	trans_x	=	lv_node->anim_track->Curve("translateX");
	IPxAnimCurve	trans_y	=	lv_node->anim_track->Curve("translateY");
	IPxAnimCurve	trans_z	=	lv_node->anim_track->Curve("translateZ");
	IPxAnimCurve	rot_x	=	lv_node->anim_track->Curve("rotateX");
	IPxAnimCurve	rot_y	=	lv_node->anim_track->Curve("rotateY");
	IPxAnimCurve	rot_z	=	lv_node->anim_track->Curve("rotateZ");

	IPxAnimCurve	pos_x		=	ge()->CreateAnimCurve();
	IPxAnimCurve	pos_y		=	ge()->CreateAnimCurve();
	IPxAnimCurve	pos_z		=	ge()->CreateAnimCurve();
	IPxAnimCurve	orient_x	=	ge()->CreateAnimCurve();
	IPxAnimCurve	orient_y	=	ge()->CreateAnimCurve();
	IPxAnimCurve	orient_z	=	ge()->CreateAnimCurve();
	IPxAnimCurve	orient_w	=	ge()->CreateAnimCurve();
	
	lv_node->anim_track->SetCurve("translateX", NULL);
	lv_node->anim_track->SetCurve("translateY", NULL);
	lv_node->anim_track->SetCurve("translateZ", NULL);
	lv_node->anim_track->SetCurve("rotateX",	NULL);
	lv_node->anim_track->SetCurve("rotateY",	NULL);
	lv_node->anim_track->SetCurve("rotateZ",	NULL);

	IPxAnimTrack	temp_track;
	
	//	fill up empty curves :
	if (!trans_x)	{
		trans_x	=	ge()->CreateAnimCurve();
		trans_x->InsertKey(0, t.x);
	}
	if (!trans_y)	{
		trans_y	=	ge()->CreateAnimCurve();
		trans_y->InsertKey(0, t.y);
	}
	if (!trans_z)	{
		trans_z	=	ge()->CreateAnimCurve();
		trans_z->InsertKey(0, t.z);
	}
	
	if (!rot_x)	{
		rot_x	=	ge()->CreateAnimCurve();
		rot_x->InsertKey(0, angles[0]);
	}
	if (!rot_y)	{
		rot_y	=	ge()->CreateAnimCurve();
		rot_y->InsertKey(0, angles[1]);
	}
	if (!rot_z)	{
		rot_z	=	ge()->CreateAnimCurve();
		rot_z->InsertKey(0, angles[2]);
	}

	//	setup animation track :	
	temp_track	=	ge()->CreateAnimTrack();
	temp_track->SetCurve("pos_x", pos_x);
	temp_track->SetCurve("pos_y", pos_y);
	temp_track->SetCurve("pos_z", pos_z);
	temp_track->SetCurve("rot_x", rot_x);
	temp_track->SetCurve("rot_y", rot_y);
	temp_track->SetCurve("rot_z", rot_z);
	
	float min_time	=	temp_track->GetMinTime();
	float max_time	=	temp_track->GetMaxTime();

	//	resample curves :	
	for (float t=min_time; t<=max_time; t+=SAMPLE_RATE) {

		float px = trans_x->Evaluate(t, 0);		
		float py = trans_y->Evaluate(t, 0);		
		float pz = trans_z->Evaluate(t, 0);		

		float ax = rot_x->Evaluate(t, 0);
		float ay = rot_y->Evaluate(t, 0);
		float az = rot_z->Evaluate(t, 0);

		
		MMatrix	fin_anim_R;
		MMatrix	fin_anim_T;

		EMatrix4	Rx	=	Matrix4RotateX( ax );
		EMatrix4	Ry	=	Matrix4RotateY( ay );
		EMatrix4	Rz	=	Matrix4RotateZ( az );
		
		if (rot_order==MTransformationMatrix::kXYZ)	R = EMatrix42MMatrix(Rx * Ry * Rz); 
		if (rot_order==MTransformationMatrix::kYZX)	R = EMatrix42MMatrix(Ry * Rz * Rx); 
		if (rot_order==MTransformationMatrix::kZXY)	R = EMatrix42MMatrix(Rz * Rx * Ry); 
		if (rot_order==MTransformationMatrix::kXZY)	R = EMatrix42MMatrix(Rx * Rz * Ry); 
		if (rot_order==MTransformationMatrix::kYXZ)	R = EMatrix42MMatrix(Ry * Rx * Rz); 
		if (rot_order==MTransformationMatrix::kZYX)	R = EMatrix42MMatrix(Rz * Ry * Rx); 
		
		T(3,0) = px;
		T(3,1) = py;
		T(3,2) = pz;

		MMatrix	finR	=	Rp.inverse() * Ro * R * Rp * Rt;
		MMatrix	finT	=	T;
		MMatrix	fin		=	finR * finT;
		
		EQuat	q	=	QuatFromMatrix( MMatrix2EMatrix4(fin) );
		EVec3	p	=	EVec3(fin(3,0), fin(3,1), fin(3,2));
		q = QuatNormalize( q );
		
		orient_x->InsertKey(t, q.x);
		orient_y->InsertKey(t, q.y);
		orient_z->InsertKey(t, q.z);
		orient_w->InsertKey(t, q.w);
		
		pos_x->InsertKey(t, p.x);
		pos_y->InsertKey(t, p.y);
		pos_z->InsertKey(t, p.z);
	}

	//	TODO DHG : compress curves!

	lv_node->anim_track->SetCurve("position_x", pos_x);
	lv_node->anim_track->SetCurve("position_y", pos_y);
	lv_node->anim_track->SetCurve("position_z", pos_z);

	lv_node->anim_track->SetCurve("orient_x", orient_x);
	lv_node->anim_track->SetCurve("orient_y", orient_y);
	lv_node->anim_track->SetCurve("orient_z", orient_z);
	lv_node->anim_track->SetCurve("orient_w", orient_w);

	return MS::kSuccess;
}


//
//	EMLevelExporter::ParseAnimCurves
//
MStatus EMLevelExporter::ParseAnimCurves( IPxLvNode lv_node, MObject &obj )
{
	MStatus	status;

	MFnDependencyNode	dg_node( obj, &status );
	MAYA_CALL( status );

	//
	//	Get all plugs connected as destination :
	//	
	MPlugArray	plug_array;
	dg_node.getConnections( plug_array );
	
	if (!lv_node->anim_track) {
		lv_node->anim_track	=	ge()->CreateAnimTrack();
	}
	
	for (uint i=0; i<plug_array.length(); i++) 
	{
		MPlug		plug	=	plug_array[i];
		MPlugArray	input_conns;
		
		//	get src-plugs connected to current plug :		
		bool has_conns = plug.connectedTo(input_conns, true, true, &status);
		
		MAYA_CALL( status );

		for (uint j=0; j<input_conns.length(); j++) {
			MPlug	curve_out	=	input_conns[j];
			MObject	curve		=	curve_out.node();

			//	check, that it is a curve :			
			if (!curve.hasFn(MFn::kAnimCurve)) {
				continue;
			}

			//
			//	Get curve properties :
			//			
			MFnAnimCurve	anim_curve( curve );
			
			uint num_keys	=	anim_curve.numKeys();
			float min_time	=	anim_curve.time(0).value();
			float max_time	=	anim_curve.time(num_keys-1).value();
			
			IPxAnimCurve	ge_curve	=	ge()->CreateAnimCurve();
			
			for (float t = min_time; t<=max_time; t+=SAMPLE_RATE) {
				double value	=	anim_curve.evaluate( MTime(t) );
			
				ge_curve->InsertKey( t, value );
			}

			//	get name of attribute which associated with
			//	current plug :			
			MFnAttribute fn_attr( plug.attribute() );
			LOGF("%s\r\n", fn_attr.name().asChar());
			
			lv_node->anim_track->SetCurve( fn_attr.name().asChar(), ge_curve );
			
			//	we have found curve, can break the loop :
			break;
		}		
	}

	
	return MS::kSuccess;
}
#endif